﻿using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using Microsoft.Win32;
using Red.Controls.Shell;
using Red.Toolkit.EngineLogsViewer.Data;
using Red.Toolkit.EngineLogsViewer.Data.Types;

namespace Red.Toolkit.EngineLogsViewer
{
    #region Editor Info

    /// <summary>
    ///     This is how we make the editor a plug-in
    /// </summary>
    [Export( typeof( IEditorInfo ) )]
    public class EditorInfo : IEditorInfo
    {
        public string Name
        {
            get { return "Engine Logs Viewer"; }
        }

        public Type WindowClass
        {
            get { return typeof( EngineLogsViewer ); }
        }

        public Type SettingsClass
        {
            get { return typeof( EngineLogsViewerSettings ); }
        }
    }

    #endregion

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class EngineLogsViewer : RedEditorWindow
    {
        #region Data

        EngineLogsViewerViewModel _modelView = new EngineLogsViewerViewModel();
        ConnectionManager _connectionManager = null;

        #endregion

        #region Constructor

        public EngineLogsViewer( IEditorHost host, EditorInstanceData instanceData, EditorSharedData sharedData )
            : base( host, instanceData, sharedData )
        {
            InitializeComponent();

            DataContext = _modelView;
        }

        #endregion

        #region Callbacks

        private void RedEditorWindow_Loaded( object sender, RoutedEventArgs e )
        {
            CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView( _modelView.LogEntries );
            view.Filter = UserFilter;

            _connectionManager = new ConnectionManager( this );
        }

        void DataWindow_Closing( object sender, CancelEventArgs e )
        {
            _connectionManager.EndConnection();
        }

        private void CheckBox_Checked( object sender, RoutedEventArgs e )
        {
            _connectionManager.SendMessage( ControlCode.GiveMeSpamLogs );
        }

        private void CheckBox_Unchecked( object sender, RoutedEventArgs e )
        {
            _connectionManager.SendMessage( ControlCode.BlockSpamLogs );
        }

        private void RedEditorWindow_ContentRendered( object sender, EventArgs e )
        {
            _connectionManager.TryToConnectWithLauncher( ( bool connectingResult ) =>
            {
                NetworkConnectionCallback( connectingResult );
            } );
        }

        private void ReconnectButton_click( object sender, RoutedEventArgs e )
        {
            _connectionManager.Reconnect( ( bool connectingResult ) =>
            {
                NetworkConnectionCallback( connectingResult );

                if ( connectingResult == true )
                {
                    RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                    {
                        if ( _showDebugLogs.IsChecked == true )
                            _connectionManager.SendMessage( ControlCode.GiveMeSpamLogs );
                        else
                            _connectionManager.SendMessage( ControlCode.BlockSpamLogs );
                    } ) );
                }
            } );
        }

        private void LoadButton_Click( object sender, RoutedEventArgs e )
        {
            try
            {
                if ( _modelView.IsConnected == true )
                    if ( RedMessageBox.Show( this, RedDialogType.YesNo, "Viewer cannot load log file when is connected with launcher.\nDo you want to disconnect?" ) == false )
                        return;

                _connectionManager.EndConnection();

                var openFileDialog = new OpenFileDialog()
                {
                    CheckFileExists = true,
                    Multiselect = false,
                    Filter = "Red log files (*.redlog)|*.redlog|Text files (*.txt)|*.txt",
                    FilterIndex = 0,
                };

                if ( openFileDialog.ShowDialog( this ) == true )
                    _modelView.SetLogEntries( LogEntryParser.ParseEntriesFromPlainText( File.ReadAllText( openFileDialog.FileName ) ) );

                RefreshItemSource();
                RefreshList();

                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                {
                    InstanceData.NotifyDispatcher.NotifyInformation( "Log is loaded correctly" );
                } ) );
            }
            catch ( Exception )
            {
                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                {
                    InstanceData.NotifyDispatcher.NotifyError( "Log cannot be loaded from file" );
                } ) );
            }
        }

        private void SaveButton_Click( object sender, RoutedEventArgs e )
        {
            try
            {
                if ( _modelView.IsConnected == true )
                    if ( RedMessageBox.Show( this, RedDialogType.YesNo, "Viewer cannot load log file when is connected with launcher.\nDo you want to disconnect?" ) == false )
                        return;

                _connectionManager.EndConnection();

                var saveFileDialog = new SaveFileDialog()
                {
                    AddExtension = true,
                    Filter = "Red log files (*.redlog)|*.redlog|Text files (*.txt)|*.txt",
                    FilterIndex = 0,
                };

                if ( saveFileDialog.ShowDialog( this ) == true )
                    File.WriteAllText( saveFileDialog.FileName, _modelView.ConvertToPlainText() );

                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                {
                    InstanceData.NotifyDispatcher.NotifyInformation( "Log is saved correctly" );
                } ) );
            }
            catch ( Exception )
            {
                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                {
                    InstanceData.NotifyDispatcher.NotifyError( "Log cannot be saved to file" );
                } ) );
            }
        }

        private void ClearButton_Click( object sender, RoutedEventArgs e )
        {
            if ( RedMessageBox.Show( this, RedDialogType.YesNo, "Do you really want to remove all logs?\nThis operation is not undoable." ) == true )
            {
                _modelView.ClearLogs();
                ResetFilter();

                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                {
                    InstanceData.NotifyDispatcher.NotifyInformation( "All logs are removed" );
                } ) );

            }
        }

        #endregion

        #region Private Methods

        private void NetworkConnectionCallback( bool connectingResult )
        {
            if ( connectingResult == false )
            {
                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                {
                    _reconnectToggleButton.IsChecked = false;
                    InstanceData.NotifyDispatcher.NotifyError( "Viewer couldn't connect to launcher" );
                } ) );
            }
            else
            {
                RedApplication.Current.Dispatcher.Invoke( DispatcherPriority.Normal, new Action( () =>
                {
                    _reconnectToggleButton.IsChecked = true;
                    InstanceData.NotifyDispatcher.NotifyInformation( "Viewer is connected with launcher" );
                } ) );
            }
        }

        private void ResetFilter()
        {
            _searchBox.Text = "";
            _severities.SelectedIndex = 7;
            _categories.SelectedIndex = 0;
            _threadIds.SelectedIndex = 0;
        }

        #endregion

        #region Filtering and searching

        private void SelectedSeverityChanged( object sender, System.Windows.Controls.SelectionChangedEventArgs e )
        {
            if ( e.AddedItems.Count > 0 )
            {
                _modelView.SelectedSeverityChanged( (string)e.AddedItems[0] );
                RefreshList();
            }
        }

        private void SelectedCategoryChanged( object sender, System.Windows.Controls.SelectionChangedEventArgs e )
        {
            if ( e.AddedItems.Count > 0 )
            {
                _modelView.SelectedCategoryChanged( (string)e.AddedItems[0] );
                RefreshList();
            }
        }

        private void SelectedThreadChanged( object sender, System.Windows.Controls.SelectionChangedEventArgs e )
        {
            if ( e.AddedItems.Count > 0 )
            {
                _modelView.SelectedThreadChanged( (string)e.AddedItems[0] );
                RefreshList();
            }
        }

        private void SearchPattern( object sender, RoutedEventArgs e )
        {
            _modelView.SearchTextChanged( _searchBox.Text );
            RefreshList();
        }

        private bool UserFilter( object item )
        {
            EngineLogAbstractEntry e = (EngineLogAbstractEntry)item;
            return e.IsCategoryVisible && e.IsTextVisible && e.IsSeverityVisible && e.IsThreadIdVisible;
        }

        private void RefreshList()
        {
            if ( _modelView != null )
            {
                CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView( _modelView.LogEntries );
                if ( view != null )
                    view.Refresh();
            }
        }

        private void RefreshItemSource()
        {
            _logEntries.ItemsSource = null;
            _logEntries.ItemsSource = _modelView.LogEntries;
        }

        private void ResetFiltersCallback( object sender, RoutedEventArgs e )
        {
            ResetFilter();
            RefreshList();
        }

        private void _searchBox_KeyDown( object sender, System.Windows.Input.KeyEventArgs e )
        {
            if ( e.Key == Key.Return )
            {
                _modelView.SearchTextChanged( _searchBox.Text );
                RefreshList();
            }
        }

        private void RedEditorWindow_Drop( object sender, DragEventArgs e )
        {
            if ( e.Data.GetDataPresent( DataFormats.FileDrop ) )
            {
                string[] files = (string[])e.Data.GetData( DataFormats.FileDrop );
                _modelView.SetLogEntries( LogEntryParser.ParseEntriesFromPlainText( File.ReadAllText( files[0] ) ) );
                RefreshItemSource();
                RefreshList();
            }
        }

        #endregion
    }
}
